package com.zengqingfa.springboot.mybatis.demo.controller;

import com.google.common.collect.Lists;
import org.redisson.Redisson;
import org.redisson.RedissonMultiLock;
import org.redisson.RedissonRedLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@RestController
public class IndexController {

    private static final Logger logger = LoggerFactory.getLogger(IndexController.class);

    @Autowired
    private RedissonClient redisson;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @RequestMapping("/deduct_stock")
    public String deductStock() {
        String lockKey = "lockKey";
        String value = UUID.randomUUID().toString();
        try {
            Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, value, 30, TimeUnit.SECONDS);
            if (!result) {
                return "稍后重试";
            }
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock")); // jedis.get("stock")
            if (stock > 0) {
                int realStock = stock - 1;
                stringRedisTemplate.opsForValue().set("stock", realStock + ""); // jedis.set(key,value)
                System.out.println("扣减成功，剩余库存:" + realStock + "");
            } else {
                System.out.println("扣减失败，库存不足");
            }
        } finally {
            if (value.equals(stringRedisTemplate.opsForValue().get(lockKey))) {
                stringRedisTemplate.delete(lockKey);
            }
        }
        return "end";
    }

    @RequestMapping("/deduct_stock2")
    public String deductStock2() throws InterruptedException {
        String lockKey = "product_001";
        RLock redissonLock = redisson.getLock(lockKey);
        try {
            // 加锁，实现锁续命功能
            redissonLock.lock();
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock")); // jedis.get("stock")
            if (stock > 0) {
                int realStock = stock - 1;
                stringRedisTemplate.opsForValue().set("stock", realStock + ""); // jedis.set(key,value)
                System.out.println("扣减成功，剩余库存:" + realStock + "");
            } else {
                System.out.println("扣减失败，库存不足");
            }
        } finally {
            if (redissonLock.isHeldByCurrentThread()) {
                redissonLock.unlock();
            }
        }
        return "end";
    }


    @RequestMapping("/redlock")
    public String redlock() throws InterruptedException {
        String lockKey = "product_001";
        //这里需要自己实例化不同redis实例的redisson客户端连接，这里只是伪代码用一个redisson客户端简化了
        RLock lock1 = redisson.getLock(lockKey);
        RLock lock2 = redisson.getLock(lockKey);
        RLock lock3 = redisson.getLock(lockKey);

        /**
         * 根据多个 RLock 对象构建 RedissonRedLock （最核心的差别就在这里）
         */
        RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);
        try {
            /**
             * 4.尝试获取锁
             * waitTimeout 尝试获取锁的最大等待时间，超过这个值，则认为获取锁失败
             * leaseTime   锁的持有时间,超过这个时间锁会自动失效（值应设置为大于业务处理的时间，确保在锁有效期内业务能处理完）
             */
            boolean res = redLock.tryLock(10, 30, TimeUnit.SECONDS);
            if (res) {
                //成功获得锁，在这里处理业务
            }
        } catch (Exception e) {
            throw new RuntimeException("lock fail");
        } finally {
            //无论如何, 最后都要解锁
            redLock.unlock();
        }

        return "end";
    }

    @RequestMapping("/getLock")
    public String getLock(String id) throws InterruptedException {
        String lockKey = "lock_key:" + id;
        RLock lock = redisson.getLock(lockKey);
        // 尝试加锁，最多等待100秒，上锁以后10秒自动解锁
        boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
        try {
            //加锁成功
            if (res) {
                //执行业务 TODO
            }

        } finally {
            //自己加锁自己解锁
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return "success";
    }

    @RequestMapping("/getMultiLock")
    public String getMultiLock(List<String> ids) throws InterruptedException {
        List<RLock> locks = new ArrayList<>();
        ids.forEach(id -> {
            String lockKey = "lock_key:" + id;
            RLock lock = redisson.getLock(lockKey);
            locks.add(lock);
        });
        //转换为数组
        RLock[] rLocks = (RLock[]) locks.toArray();
        RedissonMultiLock multiLock = new RedissonMultiLock(rLocks);
        // 尝试加锁，最多等待100秒，上锁以后10秒自动解锁
        //  boolean res = multiLock.tryLock(100, 10, TimeUnit.SECONDS);
        multiLock.lock();
        try {
            //执行业务 TODO

        } finally {
            //解锁
            multiLock.unlock();
        }
        return "success";
    }

}